/*
 * Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-11-14     cmcc       the first version
 */

#include "ns_log.h"
#include <stdio.h>
#include <string.h>

#include <os_stddef.h>
#include <os_assert.h>
#include <vfs_posix.h>

#include <sys/time.h>
#include <cJSON.h>

#include "../util/fileUtil.h"
#include "../util/httpUtil.h"

#include "ns_text_recog.h"

#define MAX_FILE_NAME_LENGTH 40

static int _ns_invoice_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Invoice *result);
static int _ns_id_card_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Id_Card *result);
static int _ns_vehicle_plate_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Vehicle_Plate *result);
static int _ns_bank_card_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Bank_Card *result);
static int _ns_ocr_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_OCR *result);

static cJSON *_card_recog(char *baseUrl, char *token, char *commandId, char *businessId, char *contentBuffer, long contentSize, char *filePath);
static cJSON *_ocr_recog(char *baseUrl, char *token, char *contentBuffer, long contentSize, char *filePath);
static int check_json(cJSON *json);

/**
 * Invoice recognition.
 *
 * @param token authorize token
 * @param contentBuffer content of invoice, only JPEG supported
 * @param contentSize length of content
 * @param result NS_Invoice
 * @return the result of invoice recognition, OS_EOK if success, else -OS_ERROR
 */
int ns_invoice_recog(char *token, char *contentBuffer, long contentSize, NS_Invoice *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_invoice_recog(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Invoice recognition by file.
 *
 * @param token authorize token
 * @param filePath file path of invoice, only JPEG supported
 * @param result NS_Invoice
 * @return the result of invoice recognition, OS_EOK if success, else -OS_ERROR
 */
int ns_invoice_recog_file(char *token, char *filePath, NS_Invoice *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(strlen(get_file_name(filePath)) <= MAX_FILE_NAME_LENGTH);
    OS_ASSERT(result);

    return _ns_invoice_recog(token, OS_NULL, -1, filePath, result);
}

/**
 * Id card recognition.
 *
 * @param token authorize token
 * @param contentBuffer content of id card, only JPEG supported
 * @param contentSize length of content
 * @param result NS_Id_Card
 * @return the result of id card recognition, OS_EOK if success, else -OS_ERROR
 */
int ns_id_card_recog(char *token, char *contentBuffer, long contentSize, NS_Id_Card *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_id_card_recog(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Id card recognition by file.
 *
 * @param token authorize token
 * @param filePath file path of id card, only JPEG supported
 * @param result NS_Id_Card
 * @return the result of id card recognition, OS_EOK if success, else -OS_ERROR
 */
int ns_id_card_recog_file(char *token, char *filePath, NS_Id_Card *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(strlen(get_file_name(filePath)) <= MAX_FILE_NAME_LENGTH);
    OS_ASSERT(result);

    return _ns_id_card_recog(token, OS_NULL, -1, filePath, result);
}

/**
 * Vehicle license plate recognition.
 *
 * @param token authorize token
 * @param contentBuffer content of vehicle plate license, only JPEG supported
 * @param contentSize length of content
 * @param result NS_Vehicle_Plate
 * @return the result of vehicle plate recognition, OS_EOK if success, else -OS_ERROR
 */
int ns_vehicle_plate_recog(char *token, char *contentBuffer, long contentSize, NS_Vehicle_Plate *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_vehicle_plate_recog(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Vehicle license plate recognition by file.
 *
 * @param token authorize token
 * @param filePath file path of vehicle license plate, only JPEG supported
 * @param result NS_Vehicle_Plate
 * @return the result of vehicle plate recognition, OS_EOK if success, else -OS_ERROR
 */
int ns_vehicle_plate_recog_file(char *token, char *filePath, NS_Vehicle_Plate *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(strlen(get_file_name(filePath)) <= MAX_FILE_NAME_LENGTH);
    OS_ASSERT(result);

    return _ns_vehicle_plate_recog(token, OS_NULL, -1, filePath, result);
}

/**
 * Band card recognition.
 *
 * @param token authorize token
 * @param contentBuffer content of bank card, only JPEG supported
 * @param contentSize length of content
 * @param result NS_Bank_Card
 * @return the result of bank card, OS_EOK if success, else -OS_ERROR
 */
int ns_bank_card_recog(char *token, char *contentBuffer, long contentSize, NS_Bank_Card *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_bank_card_recog(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Band card recognition by file.
 *
 * @param token authorize token
 * @param filePath file path of vehicle license plate, only JPEG supported
 * @param result NS_Bank_Card
 * @return the result of bank card, OS_EOK if success, else -OS_ERROR
 */
int ns_bank_card_recog_file(char *token, char *filePath, NS_Bank_Card *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(strlen(get_file_name(filePath)) <= MAX_FILE_NAME_LENGTH);
    OS_ASSERT(result);

    return _ns_bank_card_recog(token, OS_NULL, -1, filePath, result);
}

/**
 * OCR recognition.
 *
 * @param token authorize token
 * @param contentBuffer content of OCR text, only JPEG supported
 * @param contentSize length of content
 * @param result NS_OCR
 * @return the result of OCR, OS_EOK if success, else -OS_ERROR
 */
int ns_ocr_recog(char *token, char *contentBuffer, long contentSize, NS_OCR *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_ocr_recog(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * OCR recognition by file.
 *
 * @param token authorize token
 * @param filePath file path of OCR text, only JPEG supported
 * @param result NS_OCR
 * @return the result of OCR, OS_EOK if success, else -OS_ERROR
 */
int ns_ocr_recog_file(char *token, char *filePath, NS_OCR *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(strlen(get_file_name(filePath)) <= MAX_FILE_NAME_LENGTH);
    OS_ASSERT(result);

    return _ns_ocr_recog(token, OS_NULL, -1, filePath, result);
}

static int _ns_invoice_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Invoice *result)
{
    cJSON *json = _card_recog(URL_TEXT_LICENCE_RECOG, token, COMMAND_ID_INVOICE, BUSINESS_ID_INVOICE, contentBuffer, contentSize, filePath);
    if (check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->invoiceCode = strdup(cJSON_GetObjectItem(data, "invoiceCode")->valuestring);

    result->invoiceNumber = strdup(cJSON_GetObjectItem(data, "invoiceNo")->valuestring);
    result->invoiceDate = strdup(cJSON_GetObjectItem(data, "invoiceDate")->valuestring);
    result->invoiceAmount = strdup(cJSON_GetObjectItem(data, "invoiceAmount")->valuestring);
    result->capitalAmount = strdup(cJSON_GetObjectItem(data, "capitalAmount")->valuestring);

    result->buyerName = strdup(cJSON_GetObjectItem(data, "buyerName")->valuestring);
    result->buyerTaxNumber = strdup(cJSON_GetObjectItem(data, "buyerTaxNo")->valuestring);
    result->sellerName = strdup(cJSON_GetObjectItem(data, "sellerName")->valuestring);
    result->sellerTaxNumber = strdup(cJSON_GetObjectItem(data, "sellerTaxNo")->valuestring);

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_id_card_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Id_Card *result)
{
    cJSON *json = _card_recog(URL_TEXT_LICENCE_RECOG, token, COMMAND_ID_ID_CARD, BUSINESS_ID_ID_CARD, contentBuffer, contentSize, filePath);
    if (check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->address = strdup(cJSON_GetObjectItem(data, "address")->valuestring);
    result->authority = strdup(cJSON_GetObjectItem(data, "authority")->valuestring);
    result->birthday = strdup(cJSON_GetObjectItem(data, "birthday")->valuestring);
    result->effectiveDate = strdup(cJSON_GetObjectItem(data, "effectiveDate")->valuestring);

    result->ethnicity = strdup(cJSON_GetObjectItem(data, "ethnicity")->valuestring);
    result->expiryDate = strdup(cJSON_GetObjectItem(data, "expiryDate")->valuestring);
    result->gender = strdup(cJSON_GetObjectItem(data, "gender")->valuestring);
    result->idNumber = strdup(cJSON_GetObjectItem(data, "idNumber")->valuestring);

    result->name = strdup(cJSON_GetObjectItem(data, "name")->valuestring);

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_vehicle_plate_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Vehicle_Plate *result)
{
    cJSON *json = _card_recog(URL_TEXT_LICENCE_RECOG, token, COMMAND_ID_VEHICLE_PLATE, BUSINESS_ID_VEHICLE_PLATE, contentBuffer, contentSize, filePath);
    if (check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->carNumber = strdup(cJSON_GetObjectItem(data, "CarNo")->valuestring);

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_bank_card_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Bank_Card *result)
{
    cJSON *json = _card_recog(URL_TEXT_LICENCE_RECOG, token, COMMAND_ID_BANK_CARD, BUSINESS_ID_BANK_CARD, contentBuffer, contentSize, filePath);
    if (check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->cardNumber = strdup(cJSON_GetObjectItem(data, "bankCardNo")->valuestring);
    result->expiryDate = strdup(cJSON_GetObjectItem(data, "expiryDate")->valuestring);

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_ocr_recog(char *token, char *contentBuffer, long contentSize, char *filePath, NS_OCR *result)
{
    cJSON *json = _ocr_recog(URL_TEXT_OCR_RECOG, token, contentBuffer, contentSize, filePath);
    if (check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    int pages = cJSON_GetArraySize(data);
    if (pages <= 0)
    {
        cJSON_Delete(json);
        LOG_EXT_E("nothing detected");
        return -OS_ERROR;
    }

    //only first page support
    cJSON *page_json = cJSON_GetArrayItem(data, 0);
    cJSON *text_json = cJSON_GetObjectItem(page_json, "text");
    if (text_json == OS_NULL)
    {
        cJSON_Delete(json);
        LOG_EXT_E("nothing detected");
        return -OS_ERROR;
    }

    int word_count = cJSON_GetArraySize(text_json);
    NS_OCR_Item *word_array = os_malloc(sizeof(NS_OCR_Item) * word_count);
    cJSON *word_json = OS_NULL;
    int index = 0;
    cJSON_ArrayForEach(word_json, text_json)
    {
        (word_array + index)->word = strdup(cJSON_GetObjectItem(word_json, "word")->valuestring);
        cJSON *position_json = cJSON_GetObjectItem(word_json, "position");
        (word_array + index)->x1 = cJSON_GetObjectItem(position_json, "x1")->valueint;
        (word_array + index)->x2 = cJSON_GetObjectItem(position_json, "x2")->valueint;
        (word_array + index)->y1 = cJSON_GetObjectItem(position_json, "y1")->valueint;
        (word_array + index)->y2 = cJSON_GetObjectItem(position_json, "y2")->valueint;
        index++;
    }

    result->ocrItemCount = word_count;
    result->items = word_array;

    cJSON_Delete(json);
    return OS_EOK;
}

static cJSON *_card_recog(char *baseUrl, char *token, char *commandId, char *businessId, char *contentBuffer, long contentSize, char *filePath)
{
    //build url
    char url[200] = {0};
    strcat(url, baseUrl);
    strcat(url, "access_token=");
    strcat(url, token);
    LOG_EXT_D("url:%s", url);

    char *argName[2] = {0};
    argName[0] = "commandID";
    argName[1] = "businessID";

    char *argValue[2] = {0};
    argValue[0] = commandId;
    argValue[1] = businessId;

    //send http request and get JSON response
    if (filePath == OS_NULL)
    {
        /**
         * generate a temp name as format "type_time_length(low byte)", file type is JPEG supported only.
         */
        char fileName[30] = {0};
        struct timeval tv;
        gettimeofday(&tv, OS_NULL);
        sprintf(fileName, "%s_%ld_%d.jpeg", businessId, (long)tv.tv_sec, (int)(contentSize & 0xFF));
        return http_post_content_multipart_form(url, contentBuffer, contentSize, fileName, argName, argValue, 2);
    }

    return http_post_file_multipart_form(url, filePath, argName, argValue, 2);
}

static cJSON *_ocr_recog(char *baseUrl, char *token, char *contentBuffer, long contentSize, char *filePath)
{
    //build url
    char url[200] = {0};
    strcat(url, baseUrl);
    strcat(url, "access_token=");
    strcat(url, token);
    LOG_EXT_D("url:%s", url);

    //send http request and get JSON response
    if (filePath == OS_NULL)
    {
        /**
         * generate a temp name as format "type_time_length(low byte)", file type is JPEG supported only.
         */
        char fileName[30] = {0};
        struct timeval tv;
        gettimeofday(&tv, OS_NULL);
        sprintf(fileName, "ocr_%ld_%d.jpeg", (long)tv.tv_sec, (int)(contentSize & 0xFF));
        return http_post_content_multipart_form(url, contentBuffer, contentSize, fileName, OS_NULL, OS_NULL, 0);
    }

    return http_post_file_multipart_form(url, filePath, OS_NULL, OS_NULL, 0);
}

static int check_json(cJSON *json)
{
    if (json == OS_NULL)
    {
        return -OS_ERROR;
    }

    cJSON *code = cJSON_GetObjectItem(json, "code");
    if (code->valueint != 200)
    {
        LOG_EXT_E("invalid response, code:%d, msg:%s", code->valueint, (cJSON_GetObjectItem(json, "msg"))->valuestring);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    if (data == OS_NULL)
    {
        LOG_EXT_E("no data elements");
        return -OS_ERROR;
    }

    return OS_EOK;
}
